package cn.wch.blelib.host.core;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import androidx.annotation.IntDef;
import androidx.annotation.RequiresApi;

import cn.wch.blelib.host.core.callback.ConnectCallback;
import cn.wch.blelib.host.core.callback.DiscoverServiceCallback;
import cn.wch.blelib.host.core.callback.MTUCallback;
import cn.wch.blelib.host.core.callback.NotifyDataCallback;
import cn.wch.blelib.host.core.callback.PhyReadCallback;
import cn.wch.blelib.host.core.callback.PhyUpdateCallback;
import cn.wch.blelib.host.core.callback.ReadCallback;
import cn.wch.blelib.host.core.callback.RSSICallback;
import cn.wch.blelib.host.core.callback.WriteCallback;
import cn.wch.blelib.utils.FormatUtil;
import cn.wch.blelib.utils.LogUtil;

import static java.lang.Thread.sleep;

/**
 * BLE连接工具
 */
public class Connector {
    private Context context;
    private String mac;
    private ConnRuler connRuler;
    private ConnectCallback callback;
    private volatile boolean READ_NULL=false;
    private Handler handler = new Handler(Looper.getMainLooper());
    private Runnable runnable;
    private volatile boolean isRunnableAlive=false;
    private BluetoothGatt mBluetoothGatt;

    @IntDef({STATE.BLE_DISCONNECTED,STATE.BLE_CONNECTED})
    @Retention(RetentionPolicy.SOURCE)
    /**BLE连接状态*/
    public @interface  STATE{
        /**BLE连接断开*/
        int BLE_DISCONNECTED=0;
        /**BLE已连接*/
        int BLE_CONNECTED=1;
    }
    @IntDef({NOTIFY_STATE.DEFAULT,NOTIFY_STATE.PREPARE,NOTIFY_STATE.OPENED,NOTIFY_STATE.CLOSED})
    @Retention(RetentionPolicy.SOURCE)
    /**通知状态*/
    public @interface NOTIFY_STATE{
        /**默认*/
        int DEFAULT=0;
        /**准备*/
        int PREPARE=1;
        /**打开*/
        int OPENED=2;
        /**关闭*/
        int CLOSED=3;
    }
    /**连接状态*/
    private @STATE int state= STATE.BLE_DISCONNECTED;
    /**通知状态标识*/
    private @NOTIFY_STATE int notifyState=NOTIFY_STATE.DEFAULT;

    private List<BluetoothGattService> mBluetoothGattServices=null;
    private  volatile boolean rwBusy=false;
    /**接收缓存*/
    private  byte[] mRcvBuffer = new byte[20];
    /**最大数据包大小（默认20个字节）*/
    private int max_packet=20;
    private int mtuOffset=3;
    /**接收缓存大小*/
    private int mRcvLength = 0;
    /**最大OTA数据包大小（默认20个字节）*/
    private int maxOTAPacket=20;

    private DiscoverServiceCallback discoverServiceCallback=null;
    private MTUCallback mtuCallback=null;
    private RSSICallback rssiCallback=null;
    private Map<String, NotifyDataCallback> NotifyMap;
    private Map<String, WriteCallback> WriteMap;
    private Map<String, ReadCallback> ReadMap;

    private BluetoothManager manager;
    /**客户端特征配置描述符UUID，是系统提供接受通知自带的UUID。用于NORDIC 51822环境下可以扫描,连接,发送,但就是不能接收*/
    public static final UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
    /**自动发现服务*/
    private boolean autoDiscoverServices=true;
    /**发现前的睡眠时间*/
    private long sleepTimeBeforeDiscover=600;

    private PhyReadCallback phyReadCallback;
    private PhyUpdateCallback phyUpdateCallback;

    /**
     * 实例化
     * @param context 上下文
     * @param connRuler 连接规则
     * @param callback 连接状态回调
     */
    public Connector(Context context, ConnRuler connRuler, ConnectCallback callback) {
        this(context,connRuler.getMAC(),connRuler,callback);
    }
    /**
     * 实例化连接类
     * @param context 上下文
     * @param mac ble地址
     * @param connRuler 连接规则
     * @param callback 连接状态回调
     */
    public Connector(Context context, String mac, ConnRuler connRuler, ConnectCallback callback) {
        this.context=context;
        this.mac = mac;
        this.connRuler = connRuler;
        this.callback = callback;
        NotifyMap=new HashMap<>();
        WriteMap=new HashMap<>();
        ReadMap=new HashMap<>();
        manager= (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        startConnect();
    }

    /**
     * 设置新的连接规则
     * @param connRuler
     */
    public void setNewRuler(ConnRuler connRuler){
        this.connRuler = connRuler;
    }
    /**
     * 添加连接规则
     */
    private void addRunnable() {
        removeRunnable();
        isRunnableAlive=true;
        runnable = new Runnable() {
            @Override
            public void run() {
                isRunnableAlive=false;
                if(mBluetoothGatt!=null){
                    mBluetoothGatt.disconnect();
                    //2020.07.27
                    mBluetoothGatt.close();
                }
                if (callback != null) {
                    LogUtil.d("Connector "+mac+" ConnectTimeout");
                    callback.OnConnectTimeout(mac);
                }
            }
        };
        handler.postDelayed(runnable, connRuler.getConnectTimeout());
    }

    /**
     * 移除连接规则
     */
    private void removeRunnable() {
        isRunnableAlive=false;
        if (runnable != null) {
            handler.removeCallbacks(runnable);
            runnable = null;
        }
    }

    /**
     * 开始连接
     */
    public void startConnect() {
        if(callback!=null){
            callback.OnConnecting(mac);
        }
        addRunnable();
        connect();
    }

    /**
     * gatt连接
     */
    public void connect() {
        LogUtil.d(mac+"  connect");
        final BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(mac);
        if (device == null) {
            LogUtil.d("Connector",mac,"Adapter getRemoteDevice ,result is null");
            return;
        }
        int connectionState = manager.getConnectionState(device,
                BluetoothProfile.GATT);
        if (connectionState == BluetoothProfile.STATE_DISCONNECTED) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                mBluetoothGatt = device.connectGatt(context, false, bluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
            } else {
                mBluetoothGatt = device.connectGatt(context, false, bluetoothGattCallback);
            }
        }else{
            LogUtil.d("Connector",mac,"State is not disconnected before connect");
            return;
        }
    }

    /**
     * 设置Mtu
     * @param mtu
     * @param mtuCallback
     */
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void setMtu(int mtu, MTUCallback mtuCallback){
        this.mtuCallback=null;
        this.mtuCallback=mtuCallback;
        if(mBluetoothGatt!=null){
            mBluetoothGatt.requestMtu(mtu);
        }
    }

    /**
     * 读phy（物理层）
     * @param phyReadCallback
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public void readPhy(PhyReadCallback phyReadCallback){
        this.phyReadCallback=phyReadCallback;
        if(mBluetoothGatt!=null){
            mBluetoothGatt.readPhy();
        }
    }

    /**
     * 设置此应用程序的首选连接PHY。请注意，这只是一个建议，PHY更改是否会发生取决于其他应用程序首选项、本地和远程控制器功能。控制器可以覆盖这些设置。
     * @param txPhy
     * @param rxPhy
     * @param phyOptions
     * @param phyUpdateCallback
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions,PhyUpdateCallback phyUpdateCallback){
        this.phyUpdateCallback=phyUpdateCallback;
        if(mBluetoothGatt!=null){
            mBluetoothGatt.setPreferredPhy(txPhy, rxPhy, phyOptions);
        }
    }

    /**
     * 设置rssi变化回调
     * @param rssiCallback
     */
    public void setRssiCallback(RSSICallback rssiCallback){
        this.rssiCallback=null;
        this.rssiCallback=rssiCallback;
    }


    private BluetoothGattCallback bluetoothGattCallback=new BluetoothGattCallback() {
        @Override
        public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyUpdate(gatt, txPhy, rxPhy, status);
            if(phyUpdateCallback!=null){
                phyUpdateCallback.onPhyUpdate(gatt, txPhy, rxPhy, status);
            }
        }

        @Override
        public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
            super.onPhyRead(gatt, txPhy, rxPhy, status);
            if(phyReadCallback!=null){
                phyReadCallback.onPhyRead(gatt, txPhy, rxPhy, status);
            }
        }

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            BluetoothDevice device=gatt.getDevice();
            switch (newState){
                case BluetoothProfile.STATE_CONNECTING:
                    break;
                case BluetoothProfile.STATE_CONNECTED:
                    state= STATE.BLE_CONNECTED;
                    if(callback!=null){
                        callback.OnConnectSuccess(mac,new Connection(Connector.this,gatt));
                    }

                    if (Build.VERSION.SDK_INT >= 21) {
                        gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
                    }
                    /*if(connRuler.autoDiscoverServices){
                        handler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                boolean ret=discoverServices();
                                LogUtil.d("discoverServices-->"+ret);
                                //startDiscoverCountDown();
                            }
                        },connRuler.sleepTimeBeforeDiscover);
                    }else {
                        removeRunable();
                    }*/
                    if(autoDiscoverServices){
                        handler.postDelayed(() -> {
                            boolean ret=discoverServices();
                            LogUtil.d("discoverServices-->"+ret);
                            //startDiscoverCountDown();
                        },sleepTimeBeforeDiscover);
                    }else {
                        removeRunnable();
                    }
                    break;
                case BluetoothProfile.STATE_DISCONNECTING:
                    break;
                case BluetoothProfile.STATE_DISCONNECTED:
                    LogUtil.d("DISCONNECT "+status);
                    gattClose();
                    if(isRunnableAlive){
                        LogUtil.d(" try -->reConnect");
                        connect();
                        return;
                    }
                    state= STATE.BLE_DISCONNECTED;
                    closeCallback();
                    if(callback!=null){
                        callback.OnDisconnect(mac,device,status);
                    }
                    break;
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            LogUtil.d("onServicesDiscovered-->"+status);
            removeRunnable();
            cancelDiscoverCountDown();
            getSupportedServices();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                boolean connPrio = mBluetoothGatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            ReadCallback readCallback = ReadMap.get(characteristic.getUuid().toString());
            if(characteristic.getValue()==null || characteristic.getValue().length==0){
                LogUtil.d("READ NULL");
                if(readCallback!=null){
                    readCallback.OnReadNull(mac);
                }
                READ_NULL=true;
                rwBusy = false;
                mRcvLength=0;
                return;
            }
            READ_NULL=false;
            LogUtil.d("onCharacteristicRead:"+ FormatUtil.bytesToHexString(characteristic.getValue()));
            if(readCallback !=null){
                readCallback.OnReadSuccess(mac,characteristic.getValue());
            }
            mRcvLength = characteristic.getValue().length;
            System.arraycopy(characteristic.getValue(), 0,
                    mRcvBuffer, 0, characteristic.getValue().length);
            rwBusy = false;

        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            if(status!=BluetoothGatt.GATT_SUCCESS){
                LogUtil.d("write error: "+status);
                return;
            }
            rwBusy = false;
            WriteCallback writeCallback = WriteMap.get(characteristic.getUuid().toString());
            if(writeCallback!=null){
                writeCallback.OnWriteSuccess(mac,characteristic.getValue());
                LogUtil.d(mac+" onCharacteristicWrite:"+ FormatUtil.bytesToHexString(characteristic.getValue()));
            }else {
                return;
            }

        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            NotifyDataCallback notifyDataCallback = NotifyMap.get(characteristic.getUuid().toString());
            if(notifyDataCallback!=null){
                LogUtil.d("onCharacteristicChanged:"+ FormatUtil.bytesToHexString(characteristic.getValue()));
                notifyDataCallback.OnData(mac,characteristic.getValue());
            }else {
                return;
            }

        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            LogUtil.d("onDescriptorWrite--> "+"descriptor: "+descriptor.getUuid().toString()+" value: "+
                    FormatUtil.bytesToHexString(descriptor.getValue())+" status: "+status);
            if(descriptor.getUuid().toString().equalsIgnoreCase(CLIENT_CHARACTERISTIC_CONFIG.toString())){
                if(status==BluetoothGatt.GATT_SUCCESS && (FormatUtil.bytesToHexString(
                        BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE ).equalsIgnoreCase(
                                FormatUtil.bytesToHexString(descriptor.getValue())))){
                    //
                    notifyState=NOTIFY_STATE.OPENED;
                }else if(status==BluetoothGatt.GATT_SUCCESS && (FormatUtil.bytesToHexString(
                        BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE ).equalsIgnoreCase(
                                FormatUtil.bytesToHexString(descriptor.getValue())))){
                    notifyState=NOTIFY_STATE.CLOSED;
                }else {
                    notifyState=NOTIFY_STATE.DEFAULT;
                }
            }

        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            if(rssiCallback!=null){
                rssiCallback.onReadRemoteRSSI(rssi, status);
            }
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            max_packet=mtu-mtuOffset;  //最大包大小=返回的mtu-协议头字节
            maxOTAPacket=getSpecificMaxPacketForOTAAlign(mtu);
            LogUtil.d("for OTA,frame len should be set to:"+maxOTAPacket);
            mRcvBuffer = new byte[max_packet];
            if(mtuCallback!=null){
                mtuCallback.onMTUChanged(gatt, mtu, status);
            }
        }
    };

    //OTA对齐问题
    public int getSpecificMaxPacketForOTAAlign(int mtu){
        int frameMaxLen = mtu - mtuOffset;
        int dataMaxLen = frameMaxLen - 4;
        return dataMaxLen/16*16+4;
    }

    public boolean isConnected(){
        if(state== STATE.BLE_CONNECTED){
            return true;
        }
        return false;
    }

    public boolean checkGatt(){
        return mBluetoothGatt==null? false:true;
    }

    public void setRwBusy(boolean rwBusy) {
        this.rwBusy = rwBusy;
    }

    public byte[] getmRcvBuffer() {
        return mRcvBuffer;
    }

    public int getmRcvLength() {
        return mRcvLength;
    }

    public String getMac(){
        return mac;
    }

    public boolean discoverServices(){
        if (mBluetoothGatt == null)
            return false;
        LogUtil.d("Connector",mac,"discoverServices");
        return mBluetoothGatt.discoverServices();
    }

    /**
     * 发现服务
     * @param d 发现服务回调
     */
    public void discoverServices(DiscoverServiceCallback d){
        this.discoverServiceCallback=d;
        if(!discoverServices()){
            d.OnError(new Throwable("Gatt is null"));
        }
    }
    /**获取支持的服务*/
    private void getSupportedServices() {

        if(mBluetoothGatt != null) {
            mBluetoothGattServices = mBluetoothGatt.getServices();
            if(callback!=null){
                callback.OnDiscoverService(mac,mBluetoothGattServices);
            }
            if(discoverServiceCallback!=null){
                discoverServiceCallback.OnDiscover(mBluetoothGattServices);
            }
        }
    }
    /**获取服务列表*/
    public List<BluetoothGattService> getServiceList(){
        return mBluetoothGattServices;
    }

    /**
     * 等待
     * @param timeout
     * @return
     */
    private boolean waitIdle(long timeout) {
        long timeout_tmp = timeout *50+1;
        while (timeout_tmp-->0) {   //循环等待
            if (!rwBusy) {   //写入完成
                LogUtil.d("waitIdle break");
                return true;
            }
            if(!isConnected()){
                return false;
            }
            try {
                sleep(0,1000*20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        LogUtil.d("waitIdle Timeout!");
        rwBusy=false;
        return false;
    }

    /**
     * 同步读取单个
     * @param readCharacteristic
     * @return
     */
    public boolean syncReadSingle(BluetoothGattCharacteristic readCharacteristic) {
        if (mBluetoothGatt==null || readCharacteristic==null)
            return false;
        if(!isConnected()){
            return false;
        }
        setRwBusy(true);
        mBluetoothGatt.readCharacteristic(readCharacteristic);
        return waitIdle(connRuler.getReadTimeout());
    }

    /**
     * 同步重复读取
     * @param readCharacteristic
     * @return
     */
    public boolean syncReadRepeat(BluetoothGattCharacteristic readCharacteristic) {
        for(int i=0;i<connRuler.getReadNullRetryCount();i++){
            LogUtil.d("syncReadRepeat-->"+i);
            if(syncReadSingle(readCharacteristic)){
                if(READ_NULL){
                    READ_NULL=false;
                }else{
                    READ_NULL=false;
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 同步写入特征信息
     * @param writeCharacteristic
     * @param data 写入的数据
     * @return
     */
    public boolean syncWriteCharacteristic(BluetoothGattCharacteristic writeCharacteristic, byte[] data) {
        LogUtil.d("try write :"+ FormatUtil.bytesToHexString(data));
        if (mBluetoothGatt==null || writeCharacteristic==null)
            return false;
        if(!isConnected()){
            return false;
        };
        writeCharacteristic.setValue(data);
        setRwBusy(true);
        boolean ok = mBluetoothGatt.writeCharacteristic(writeCharacteristic);

        if (ok){
            LogUtil.d("wait...");
            ok = waitIdle(connRuler.getWriteTimeout());
        }
        LogUtil.d("waitIdle "+ok);
        return ok;
    }

    /**
     * 异步写特征信息
     * @param writeCharacteristic 写特征对象
     * @param data
     * @param writeCallback
     */
    @Deprecated
    public void asyncWriteCharacteristic(BluetoothGattCharacteristic writeCharacteristic, byte[] data,
                                         WriteCallback writeCallback) {
        if(writeCallback==null){
            return;
        }
        LogUtil.d("try write :"+ FormatUtil.bytesToHexString(data));
        if (mBluetoothGatt==null || writeCharacteristic==null){
            writeCallback.OnError(mac,new Throwable("GATT or Character is null"));
            return ;
        }
        if(!isConnected()){
            writeCallback.OnError(mac,new Throwable("device is not connected"));
            return ;
        }
        WriteMap.put(writeCharacteristic.getUuid().toString(),writeCallback);
        writeCharacteristic.setValue(data);
        boolean ok = mBluetoothGatt.writeCharacteristic(writeCharacteristic);
        if (!ok){
            LogUtil.d("write fail");
            writeCallback.OnWriteFail(mac,data);
        }
    }

    /**
     * 异步写特征写信息
     * @param writeCharacteristic
     * @param data
     * @return
     */
    @Deprecated
    public boolean asyncWriteCharacteristic(BluetoothGattCharacteristic writeCharacteristic, byte[] data) {
        LogUtil.d("try write :"+ FormatUtil.bytesToHexString(data));
        if (mBluetoothGatt==null || writeCharacteristic==null){
            return false;
        }
        if(!isConnected()){
            return false;
        }
        writeCharacteristic.setValue(data);
        return mBluetoothGatt.writeCharacteristic(writeCharacteristic);
    }

    /**
     * 异步读数据
     * @param readCharacteristic
     * @param readCallback
     */
    public void asyncReadCharacteristic(BluetoothGattCharacteristic readCharacteristic,ReadCallback readCallback) {
        if(readCallback==null){
            return;
        }
        if (mBluetoothGatt==null || readCharacteristic==null) {
            readCallback.OnError(mac,new Throwable("GATT or Character is null"));
            return;
        }
        if(!isConnected()){
            readCallback.OnError(mac,new Throwable("device is not connected"));
            return ;
        }
        ReadMap.put(readCharacteristic.getUuid().toString(),readCallback);
        mBluetoothGatt.readCharacteristic(readCharacteristic);
    }

    /**
     * 检查是否启用特征改变通知
     * @param characteristic
     * @return
     */
    public boolean isNotificationEnabled(BluetoothGattCharacteristic characteristic) {
        if (!checkGatt())
            return false;
        BluetoothGattDescriptor clientConfig = characteristic
                .getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
        if (clientConfig == null)
            return false;
        return clientConfig.getValue() == BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
    }

    /**
     * 启用特征改变通知
     * @param enable 启用？
     * @param characteristic
     * @return
     */
    public synchronized boolean enableNotify(boolean enable,BluetoothGattCharacteristic characteristic){
        LogUtil.d("try--->change notification "+enable);
        if(characteristic==null){
            return false;
        }
        if (!checkGatt())
            return false;
        if (mBluetoothGatt.setCharacteristicNotification(characteristic, enable)) {
            boolean ret=false;
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG);
            if(descriptor != null) {

                byte[] val = enable ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE;
                descriptor.setValue(val);
                //初始化
                notifyState=NOTIFY_STATE.PREPARE;
                //开始写
                ret=mBluetoothGatt.writeDescriptor(descriptor);
                if(!ret){
                    notifyState=NOTIFY_STATE.DEFAULT;
                    LogUtil.d("writeDescriptor fail");
                    return false;
                }
                //等待onDescriptorWrite回调
                ret=false;
                int count=0;
                while (count<150){
                    if(enable && notifyState==NOTIFY_STATE.OPENED){
                        //打开通知成功
                        ret=true;
                        break;
                    }else if(!enable && notifyState==NOTIFY_STATE.CLOSED){
                        //关闭通知成功
                        ret=true;
                        break;
                    }

                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count++;
                }
                notifyState=NOTIFY_STATE.DEFAULT;
            }
            LogUtil.d("Change Notify result: "+ret);
            return ret;
        }else{
            return false;
        }
    }

    /**
     * 设置特征变换通知侦听
     * @param characteristic
     * @param notifyDataCallback
     */
    public synchronized void setNotifyListener(BluetoothGattCharacteristic characteristic,
                                               NotifyDataCallback notifyDataCallback){
        LogUtil.d("setNotifyListener: "+characteristic.getUuid().toString());
        NotifyMap.put(characteristic.getUuid().toString(),notifyDataCallback);
    }

    /**
     * 设置特征改变通知侦听器
     * @param characteristic
     * @param notifyDataCallback
     * @param forceOpen 强制打开特征改变通知
     * @return
     */
    public synchronized boolean setNotifyListener(BluetoothGattCharacteristic characteristic,
                                                  NotifyDataCallback notifyDataCallback,boolean forceOpen){
        LogUtil.d("setNotifyListener: "+characteristic.getUuid().toString());
        if(!forceOpen){
            NotifyMap.put(characteristic.getUuid().toString(),notifyDataCallback);
            return true;
        }else {
            //强制打开通知
            if(!isNotificationEnabled(characteristic)){
                //当前未打开
                int retryCount=0;
                boolean ret=false;
                NotifyMap.put(characteristic.getUuid().toString(),notifyDataCallback);
                while (retryCount<10){
                    if(enableNotify(true,characteristic)){
                       ret=true;
                       break;
                    }
                    retryCount++;
                }
                if(!ret){
                    NotifyMap.remove(characteristic.getUuid().toString());
                    notifyDataCallback.OnError(mac,new Throwable("open notify function fail"));
                    return false;
                }
                return true;
            }else {
                LogUtil.d("notify has opened");
                NotifyMap.put(characteristic.getUuid().toString(),notifyDataCallback);
                return true;
            }
        }

    }

    /**
     * 获取最大数据包
     * @return
     */
    public int getMaxPacket(){
        return max_packet;
    }

    public int getMtu(){
        return max_packet+mtuOffset;
    }

    public void disconnect(){
        if(state== STATE.BLE_CONNECTED && checkGatt()){
            mBluetoothGatt.disconnect();
        }

    }

    public void closeCallback(){
        NotifyMap.clear();
        ReadMap.clear();
        WriteMap.clear();
    }

    public void close(){
        if(isRunnableAlive){
            removeRunnable();
        }
        gattClose();
    }

    private void gattClose(){
        if(checkGatt()){
            mBluetoothGatt.disconnect();
            mBluetoothGatt.close();
            mBluetoothGatt=null;

        }
    }

    Runnable discoverRunnable;
    private void startDiscoverCountDown(){
        discoverRunnable= () -> {
            boolean b = discoverServices();
            LogUtil.d("startDiscoverCountDown-->"+b);
        };
        handler.postDelayed(discoverRunnable,500);
    }

    private void cancelDiscoverCountDown(){
        if(discoverRunnable!=null){
            handler.removeCallbacks(discoverRunnable);
        }
    }

}
